package nape.phys;
import cx.FastList;
import nape.callbacks.Callbackable;
import nape.dynamics.SubArbiters;
import nape.dynamics.ObjArb;
import nape.phys.Properties;
import nape.geom.VecMath;
import nape.geom.AABB;
import nape.Config;
import nape.Const;
import nape.phys.Body;
import nape.space.Space;
import flash.display.DisplayObject;






















class PhysObj extends Callbackable {
	
	static var nextId:Int = 0;
	public var id    :Int;
	
	
	
	vec_new(public,p); 
	vec_new(public,pre_p); 
	
	vec_new(public,v); 
	vec_new(public,f); 
	vec_new(public,bv); 
	
	
	
	public var mass:Float;
	public var imass:Float;
	public var smass:Float; 
	public var gmass:Float; 
	
	public var cmass:Float; 
	public var ogmass:Float; 
	 
	public var properties:Properties;
	
	public var data:Dynamic;
	
	public var visible:Bool;
	public var pvisible:Bool;
	public var aabb:AABB;
	
	
	
	public var isStatic:Bool; 
	
	
	public var isBody:Bool;
	public var body:Body;
	public var particle:Particle;
	
	
	
	public var hasGraphic:Bool;
	public var graphic:DisplayObject;
	public var rotateGraphic:Bool;
	vec_new(public,graphic_delta);
	
	
	
	
	
	public var p_objarb:CxFastList(ObjArb);
	
	
	public var p_constraints:CxFastList(Constraint);
	
	public var added_to_space:Bool;
	public var space:Space;
	
	
	
	
	public var kinetic:Float;
	public var evslp:Int;
	public var evrad:Float;
	
	public var live :Bool;
	public var woken:Bool;
	public var sleep:Bool;
	public var stamp:Int;
	public var plive:Bool;
	
	
	
	
	public var cbSleep      :Bool;
	public var cbWake       :Bool;
	public var cbOutOfBounds:Bool;
	public var cbShow       :Bool;
	public var cbHide       :Bool;
	
	public var cbSleepDef      :Bool;
	public var cbWakeDef       :Bool;
	public var cbOutOfBoundsDef:Bool;
	public var cbShowDef       :Bool;
	public var cbHideDef       :Bool;
	
	
	
	
	public var imoment:Float;
	public var smoment:Float; 
	public var cmoment:Float; 
	public var moment:Float;
	public var a:Float;
	public var pre_a:Float;
	public var w:Float;
	public var t:Float;
	vec_new(public,rot);
	
	public var group_obj:Group;
	
	
	
	public function new(X:Float,Y:Float,BODY:Bool,?PROP:Properties) {
		vec_set(p,X,Y); vec_set(pre_p,p);
		vec_set(v,0,0); vec_set(f,0,0); vec_set(bv,0,0);
		aabb = new AABB();
		
		properties = if(PROP==null) Config.DEFAULT_PROPERTIES else PROP;
		
		isBody = BODY;
		stamp = 0;
		hasGraphic = false;
		rotateGraphic = true;
		vec_set(graphic_delta,0,0);
		
		id = nextId++;
		
		kinetic = 0;
		evslp = 1;
		cbType = 0;
		
		cbSleepDef = cbWakeDef = cbOutOfBoundsDef = cbShowDef = cbHideDef = true;
		
		
		p_objarb = new CxFastList(ObjArb)();
		
		p_constraints = new CxFastList(Constraint)();
	}
	
	
	
	public function update():Void;
	
	public inline function updateVelocity_linear(dt:Float) {
		evslp = -1;
		if(smass!=0.0) {
			var d = dt*imass;
			vec_muleq(v,properties.lfdt); vec_addeq(v,f,d);
		}
		vec_set(f,0,0);
	}
	public inline function updatePosition_linear(dt:Float) {
		vec_set(pre_p,p);
		if(smass!=0.0) {
			vec_addeq(p,v,dt); vec_addeq(p,bv);
			vec_set(bv,0,0);
		}
		if(hasGraphic) {
			if(isBody) {
				graphic.x = px + vec_cross(graphic_delta,rot);
				graphic.y = py + vec_dot  (graphic_delta,rot);
			}else {
				vec_set(graphic.,p);
				vec_addeq(graphic.,graphic_delta);
			}
		}
	}
	
	
	
	public inline function sleep_base() {
		vec_set(v,0,0);
		imass = 0;
		sleep = true;
		woken = false;
	}
	public inline function wake_base() {
		sleep = false;
		imass = smass;
	}
	
	
	
	public inline function stopMovement() {
		mass  = Const.FMAX;
		imass = smass = 0;
		
		
		vec_set(v,0,0); vec_set(f,0,0); vec_set(bv,0,0);
	}
	public inline function allowMovement() {
		mass = 1.0/cmass;
		
		imass = smass = cmass;
	}
	public inline function stopAll() {
		stopMovement();
		if(isBody)
			cast (this,Body).stopRotation();
	}
	public inline function allowAll() {
		allowMovement();
		if(isBody)
			cast (this,Body).allowRotation();
	}
	
	
	
	public inline function assignGraphic(g:DisplayObject) {
		graphic = g;
		hasGraphic = g!=null;
	}
	
	
	
	public inline function activeMotion() {
		if(evslp==-1) {
			evslp = 1;
			if(vec_lsq(v)>=Config.REST_LINEAR) evslp = 0;
			if(evslp!=0 && w*w*evrad>=Config.REST_ANGULAR) evslp = 0;
			
			
			
			
			
			
			
			if(space!=null) {
				if(evslp!=0) {
					var da = (a - pre_a)/space._dt;
					if(da*da*evrad>Config.REST_BIAS_ANGULAR) evslp = 0;
				}
				if(evslp!=0) {
					vec_new(dp); vec_set(dp,p); vec_subeq(dp,pre_p); vec_diveq(dp,space._dt);
					if(vec_lsq(dp)>Config.REST_BIAS_LINEAR) evslp = 0;
				}
			}
		}
		return evslp == 0;
	}
	
	
	
	public inline function applyGlobalForce(fx:Float,fy:Float,gx:Float,gy:Float) {
		vec_addeq(this.f,f);
		if(isBody) {
			vec_new(r); vec_sub(g,p,r);
			t += vec_cross(r,f);
		}
	}
	
	public inline function applyRelativeForce(fx:Float,fy:Float,rx:Float,ry:Float) {
		vec_addeq(this.f,f);
		if(isBody)
			t += vec_cross(r,f);
	}
	
	
	
	public inline function setPos(x:Float, y:Float, ?angle:Float):Void {
		vec_set(p,);
		if (isBody && angle != null) body.setAngle(angle);
	}
	public inline function setVel(x:Float, y:Float, ?w:Float):Void {
		vec_set(v,);
		if (isBody && w != null) this.w = w;
	}
}